// From https://forums.alliedmods.net/showpost.php?p=341680&postcount=6

////////////////////////////////////////////////////////////////////
//                    VFuncHook.h
//
////////////////////////////////////////////////////////////////////
#ifndef LOJO_VFUNCHOOK_H
#define LOJO_VFUNCHOOK_H

#ifdef _WIN32
# pragma once
#endif

/////////////////////////////////////////////////////////
// First we need to make sure they're using a compiler
// I know this works on.  Pretty much everybody coding
// for HL2 uses either Microsoft Visual C++ .NET 2003
// (aka MSVC 7.1) or GCC for Linux.  These are the
// two compilers I know this works on.  It may work
// for others, but it is not tested.
/////////////////////////////////////////////////////////
#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__VECTOR_C) && !defined(__ICL) && !defined(__BORLANDC__)
# if (_MSC_VER < 1310)	// Less than 7.1
#  error Unsuported Compiler
# endif
# define LOJO_MSVC	// Using MSVC
#elif defined(__GNUC__) && defined(_LINUX)
# define LOJO_GCC	// Using GCC (only for linux, don't support g++ on windows) 
#else
# error Unsupported Compiler
#endif

/////////////////////////////////////////////////////////
// Declare our empty class that we use for calling.
// It is imperative that this class is never defined
// elsewhere.  For MSVC we'll declare it as a multiple
// inheritance so that its member function pointers
// are identical to GCC's (as far as we're concerned)
/////////////////////////////////////////////////////////
#ifdef LOJO_MSVC
  class __multiple_inheritance VFuncEmptyClass;
# define VFUNC_ADJ	0			// Since Linux(GCC) vtable indexes are one more than Windows(MSVC)
#else // LOJO_GCC
  class VFuncEmptyClass { };
# define VFUNC_ADJ	1			// Since Linux(GCC) vtable indexes are one more than Windows(MSVC)
#endif

/////////////////////////////////////////////////////////
// Includes and such needed for MemProtect
/////////////////////////////////////////////////////////
#ifdef LOJO_MSVC
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#else // LOJO_GCC
# include <sys/mman.h>
# ifndef PAGESIZE
#  define PAGESIZE 4096
# endif
#endif

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Now declare our Member Function Pointer structure,
// which will allow us to trick the compiler to allow
// us to call the functions we want.
//
// For GCC this structure looks like this:
// 
// struct GCC_MFI {
//     union {
//         void *addr;				// Address of the function to call
//         int vtable_index_2_1;	// (vtable_index * 2) + 1 -> always odd.  Zero if non-virtual function
//     };
//     int delta;					// Ammount to add to the 'this' pointer for polymorphic classes
// };
//
// So if the pointer is to a virtual function, vtable_index_2_1 will be odd, otherwise it's zero.  The thing is, GCC
// will always look up the vfunc in the vtable, so we're going to tell it that it's a non-virtual function instead.
// This will, for our purposes, make the GCC_MFI identical to the MSVC_MFI that we're using.  There are actually
// four versions of the MSVC_MFP, but we're going to use only one (that's why we declared VFuncEmptyClass
// using the __multiple_inheritance keyword).  Here's the MSVC Member Function Pointer layout:
//
// struct MSVC_MFI {
//     void *addr;			// Address of the function to call
//     int delta;			// Ammount to add to the 'this' pointer for polymorphic classes
// };
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Lojo_vfuncs {

struct MFI {
	void *addr;
	int delta;
};
// This is a trick to make sure that the correct member function pointers are being used.
typedef int ERROR_Incorrect_Member_Function_Pointer_size[sizeof(MFI)==sizeof(void (VFuncEmptyClass::*)( )) ? 1 : -1];

/////////////////////////////////////////////////////////
// Now we start declaring our templates.  First we'll
// declare a macro to make it easier to define our clases,
// then we'll define our classes.
/////////////////////////////////////////////////////////
#define DEFINE_VFUNC_CLASS( number, returnType, typedef_params, call_params1, call_params2 ) \
	class VFunc##number { \
		typedef returnType (VFuncEmptyClass::*VFuncType)typedef_params; \
		union { \
			VFuncType func; \
			MFI info; \
		} Func; \
	public: \
		VFunc##number( ) { Func.func = 0; } \
		\
		inline void Get( void *thisPtr, int vtable_index, int this_adjustor = 0, int vtable_offset = 0 ) { \
			void **vtable = *(reinterpret_cast<void ***>(reinterpret_cast<char *>(thisPtr) + this_adjustor + vtable_offset)); \
			Func.info.addr = vtable[vtable_index + VFUNC_ADJ]; \
			Func.info.delta = this_adjustor; \
		} \
		inline void Set( void *func, int this_adjustor = 0 ) { \
			Func.info.addr = func; \
			Func.info.delta = this_adjustor; \
		} \
		inline returnType operator ( ) call_params1 const { \
			return (returnType)(reinterpret_cast<VFuncEmptyClass *>(thisPtr)->*Func.func) call_params2; \
		} \
		inline bool operator ! ( ) const { return !Func.info.addr; } \
		inline bool empty( ) const { return !Func.info.addr; } \
	private: \
		typedef returnType (*StaticFuncPtr) typedef_params; \
		typedef struct SafeBoolStruct { \
			int a_data_pointer_to_this_is_0_on_buggy_compilers; \
			StaticFuncPtr m_nonzero; \
		} UselessTypedef; \
		typedef StaticFuncPtr SafeBoolStruct::*unspecified_bool_type; \
	public: \
		operator unspecified_bool_type( ) const { \
			return empty( ) ? 0 : &SafeBoolStruct::m_nonzero; \
		} \
	};

// N==0
template <class R>
DEFINE_VFUNC_CLASS( 0, R, ( ), ( void *thisPtr ), ( ) );
// N==1
template <class P1, class R>
DEFINE_VFUNC_CLASS( 1, R, ( P1 ), ( void *thisPtr, P1 p1 ), ( p1 ) );
// N==2
template <class P1, class P2, class R>
DEFINE_VFUNC_CLASS( 2, R, ( P1, P2 ), ( void *thisPtr, P1 p1, P2 p2 ), ( p1, p2 ) );
// N==3
template <class P1, class P2, class P3, class R>
DEFINE_VFUNC_CLASS( 3, R, ( P1, P2, P3 ), ( void *thisPtr, P1 p1, P2 p2, P3 p3 ), ( p1, p2, p3 ) );
// N==4
template <class P1, class P2, class P3, class P4, class R>
DEFINE_VFUNC_CLASS( 4, R, ( P1, P2, P3, P4 ), ( void *thisPtr, P1 p1, P2 p2, P3 p3, P4 p4 ), ( p1, p2, p3, p4 ) );
// N==5
template <class P1, class P2, class P3, class P4, class P5, class R>
DEFINE_VFUNC_CLASS( 5, R, ( P1, P2, P3, P4, P5 ), ( void *thisPtr, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 ), ( p1, p2, p3, p4, p5 ) );
// N==6
template <class P1, class P2, class P3, class P4, class P5, class P6, class R>
DEFINE_VFUNC_CLASS( 6, R, ( P1, P2, P3, P4, P5, P6 ), ( void *thisPtr, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 ), ( p1, p2, p3, p4, p5, p6 ) );
// N==7
template <class P1, class P2, class P3, class P4, class P5, class P6, class P7, class R>
DEFINE_VFUNC_CLASS( 7, R, ( P1, P2, P3, P4, P5, P6, P7 ), ( void *thisPtr, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7 ), ( p1, p2, p3, p4, p5, p6, p7 ) );

}	// namespace Lojo_vfuncs
///////////////////////////////////////////////////////
// Now we'll combine all the vFunc class into one,
// to allow function type syntax declarations
///////////////////////////////////////////////////////
template <class Signature>
class VFunc;

// N==0
template <class R>
class VFunc<R ( )> : public Lojo_vfuncs::VFunc0<R> { };
// N==1
template <class P1, class R>
class VFunc<R ( P1 )> : public Lojo_vfuncs::VFunc1<P1, R> { };
// N==2
template <class P1, class P2, class R>
class VFunc<R ( P1, P2 )> : public Lojo_vfuncs::VFunc2<P1, P2, R> { };
// N==3
template <class P1, class P2, class P3, class R>
class VFunc<R ( P1, P2, P3 )> : public Lojo_vfuncs::VFunc3<P1, P2, P3, R> { };
// N==4
template <class P1, class P2, class P3, class P4, class R>
class VFunc<R ( P1, P2, P3, P4 )> : public Lojo_vfuncs::VFunc4<P1, P2, P3, P4, R> { };
// N==5
template <class P1, class P2, class P3, class P4, class P5, class R>
class VFunc<R ( P1, P2, P3, P4, P5 )> : public Lojo_vfuncs::VFunc5<P1, P2, P3, P4, P5, R> { };
// N==6
template <class P1, class P2, class P3, class P4, class P5, class P6, class R>
class VFunc<R ( P1, P2, P3, P4, P5, P6 )> : public Lojo_vfuncs::VFunc6<P1, P2, P3, P4, P5, P6, R> { };
// N==7
template <class P1, class P2, class P3, class P4, class P5, class P6, class P7, class R>
class VFunc<R ( P1, P2, P3, P4, P5, P6, P7 )> : public Lojo_vfuncs::VFunc7<P1, P2, P3, P4, P5, P6, P7, R> { };

//////////////////////////////////////////////////////////////////////////////////////
// And now for the hooking class.  Basically this class just inserts a given address
// into the vtable of the target.  Hooks are declares using single inheritance, but
// we don't really have to worry about MSVC's four different types of MFP's and the
// one type for GCC, because they all have one thing in common when dealing with
// non-virtual functions: the beginning of the pointer contains the address to the
// function to call.  We'll use this to our advantage to simplify things.
//////////////////////////////////////////////////////////////////////////////////////
namespace Lojo_vfuncs {

class VFuncHook {
	void *orig_func;		// Address of the origonal function that was overridden by this hook
	void *hook_func;		// Address of the function we inserted into the vtable
	void **vtable;			// Pointer to the target vtable that this is inserted into
	int m_vtable_index;		// Index on the vtable that this is inserted into
	int delta;				// Ammount to add to the 'this' pointer to get the proper pointer for the func (used to make a VFunc object)

public:
	VFuncHook( ) : orig_func( 0 ), hook_func( 0 ), vtable( 0 ), m_vtable_index( 0 ), delta( 0 ) { }

	template <class MFP>
	bool Insert( MFP func, void *thisPtr, int vtable_index, int this_adjustor = 0, int vtable_offset = 0 ) {
		// Make sure we're not already inserted, orig_func will be non-zero if we've inserted
		if ( orig_func ) return false;

		// Now a liitle hack to get the address of the function that the member function pointer is pointing at
		union {
			MFP temp;
			void *addr;
		} u;
		u.temp = func;
		hook_func = u.addr;

		// Check to make sure we have valid paramters
		if ( !hook_func || !thisPtr || !vtable_index ) return false;

		// Get the vtable we're hooknig into, and the offset
		vtable = *(reinterpret_cast<void ***>(reinterpret_cast<char *>(thisPtr) + this_adjustor + vtable_offset));
		m_vtable_index = vtable_index + VFUNC_ADJ;		// Adjust for GCC being 1 more than MSVC

		// Now unprotect the vtable, so we can insert our function into there
		if ( !MemProtect( (void *)vtable, (m_vtable_index + 1) * sizeof(void *), false ) ) return false;

		// Insert out hook and store the old function's address
		orig_func = vtable[m_vtable_index];
		vtable[m_vtable_index] = hook_func;

		// Reprotect the vtable, to provent writing to it
		MemProtect( (void *)vtable, (m_vtable_index + 1) * sizeof(void *), true );

		// Save the this_adjustor for making a vFunc object
		delta = this_adjustor;

		// All done
		return true;
	}
	bool Remove( bool bForceRemove = false ) {
		// First, make sure we've inserted
		if ( !orig_func ) return false;

		// Now we need to make sure that the function in the vtable is the one we inserted
		// However, if bForceRemove is true, we're going to replace the vtable with the origonal function anyway
		if ( !bForceRemove && (vtable[m_vtable_index] != hook_func) ) return false;

		// Unprotect the memory
		if ( !MemProtect( (void *)vtable, (m_vtable_index + 1) * sizeof(void *), false ) ) return false;

		// Replace the vtable entry with the old address, and reset our orig_func to 0
		vtable[m_vtable_index] = orig_func;
		orig_func = hook_func = 0;

		// Reprotect the memory
		MemProtect( (void *)vtable, (m_vtable_index + 1) * sizeof(void *), true );

		// Reset vtable and m_vtable_index back to 0
		vtable = 0;
		m_vtable_index = 0;

		// And we're done
		return true;
	}
	inline bool operator ! ( ) const { return !orig_func; }

	template <class Signature>
	const VFunc<Signature> MakeVFunc( ) const {
		VFunc<Signature> temp;
		temp.Set( orig_func, delta );
		return temp;
	}

private:
	inline bool MemProtect( void *pMemory, unsigned int uiLen, bool bLock ) {
#ifdef _WIN32
		DWORD IDontCare;
		return !!VirtualProtect( pMemory, uiLen, (bLock ? PAGE_READONLY : PAGE_EXECUTE_READWRITE), &IDontCare );
#else // Linux
		unsigned long p = (unsigned long)pMemory & ~(PAGESIZE-1);
		return !mprotect( (void *)p, (unsigned long)pMemory - p + uiLen, PROT_READ | PROT_EXEC | (bLock ? 0 : PROT_WRITE) );
#endif
	}
};

} // namespace Lojo_vfuncs

///////////////////////////////////////////////////////////
// Now the macros for declaring a usable VFuncHook object
///////////////////////////////////////////////////////////
#define DeclareVFuncHook( hookName, functionName, returnType, proto ) \
	class hookName : public Lojo_vfuncs::VFuncHook { \
	public: \
		returnType functionName proto; \
	}; \

#define DefineVFuncHook( hookName, functionName, returnType, proto ) \
	returnType hookName::functionName proto

// Clean up after ourselves
#undef LOJO_GCC
#undef LOJO_MSVC
#undef DEFINE_VFUNC_CLASS
#undef VFUNC_ADJ

#endif // LOJO_VFUNCHOOK_H